package top.szhome.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.szhome.framework.core.tools.exception.SzhomeException;
import top.szhome.framework.core.tools.util.ObjectUtil;
import top.szhome.framework.starter.db.support.SimpleServiceImpl;
import top.szhome.system.convert.UserConverter;
import top.szhome.system.dto.UserDTO;
import top.szhome.system.entity.User;
import top.szhome.system.entity.UserRole;
import top.szhome.system.mapper.UserMapper;
import top.szhome.system.service.ParamService;
import top.szhome.system.service.UserRoleService;
import top.szhome.system.service.UserService;
import top.szhome.system.vo.ParamVO;
import top.szhome.system.vo.UserVO;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户 服务实现
 *
 * @author 唐加利
 * @date 2023/07/31
 **/
@AllArgsConstructor
@Service
@Slf4j
public class UserServiceImpl extends SimpleServiceImpl<UserMapper, UserConverter, UserDTO, User, UserVO> implements UserService {

    private final ParamService paramService;

    private final UserRoleService userRoleService;

    /**
     * 重置密码的系统参数
     */
    public static final String PARAM_RESET_PWD = "PWD_WHEN_RESET";

    /**
     * 重置密码时如未配置系统参数使用的默认密码
     */
    public static final String DEFAULT_RESET_PWD = "1qaz@WSX";

    @Override
    public UserVO login(String username, String password) {
        User user = getBaseMapper().selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUsername, username));
        if(user == null || !user.getPassword().equals(password)){
            throw new SzhomeException("用户或密码错误");
        }

        return getBaseConvert().to(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifyPwd(Long userId, String oldPassword, String newPassword) {
        UserVO user = get(userId);
        if(user == null){
            throw new SzhomeException("500", "ID为" + userId + "的用户不存在");
        }

        if(ObjectUtil.equals(oldPassword, user.getPassword())){
            throw new SzhomeException("500", "原密码输入错误");
        }

        User u = new User();
        u.setId(userId);
        u.setPassword(newPassword);
        updateById(u);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPwd(List<Long> userIds) {
        List<User> users = listByIds(userIds);
        if(ObjectUtil.isEmpty(users)){
            throw new SzhomeException("500", "未查询到任何用户信息，无法重置密码");
        }

        // TODO 随机密码，并发送邮件
        ParamVO param = paramService.getByCode(PARAM_RESET_PWD);
        String password = ObjectUtil.isNull(param) ? DEFAULT_RESET_PWD : param.getValue();

        User u = new User();
        u.setPassword(password);
        update(u, Wrappers.lambdaUpdate(User.class).in(User::getId, users.stream().map(User::getId).collect(Collectors.toList())));
    }

    @Override
    public List<Long> getUserRoles(Long userId) {
        return userRoleService.getRolesOfUser(userId);
    }

    @Override
    public void grantRoles(Long userId, List<Long> roleIds){
        // 检索用户所有已分配角色情况查询条件
        LambdaQueryWrapper<UserRole> wrapper = Wrappers.lambdaQuery(UserRole.class).eq(UserRole::getUserId, userId);

        // 全部取消分配，简化处理
        if(ObjectUtil.isEmpty(roleIds)){
            userRoleService.remove(wrapper);
            return;
        }

        // 获取到每个角色已有的菜单集
        List<UserRole> userRoles = userRoleService.list(wrapper);

        // 需要移除的已分配角色
        Set<Long> roleIdSet = new HashSet<>(roleIds);
        List<Long> remove = userRoles.stream()
                .filter(role -> !roleIdSet.contains(role.getRoleId()))
                .map(UserRole::getId)
                .collect(Collectors.toList());
        Optional.ofNullable(remove).ifPresent(userRoleService::removeBatchByIds);

        // 需要新增的角色分配
        Set<Long> owned = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toSet());
        List<UserRole> add = roleIdSet.stream().filter(roleId -> !owned.contains(roleId)).map(roleId -> {
            UserRole userRole = new UserRole();
            userRole.setUserId(userId);
            userRole.setRoleId(roleId);
            return userRole;
        }).collect(Collectors.toList());
        Optional.ofNullable(add).ifPresent(userRoleService::saveBatch);
    }
}
